﻿/**
    作者：张峰
    版本：AS3.0

    功能：获取当前声音频谱值

*/
/**
 * 获取当前声音频谱
 * 一个用于指示是否首先对声音数据执行 Fourier 转换的布尔值。 将此参数设置为 true 会导致方法返回的是频谱而不是原始声音波形。 在频谱中，左侧表示的是低频，右侧表示的是高频。 
 */
package zf.media.audio
{
	import flash.media.SoundMixer;
	import flash.utils.ByteArray;


	/**
	 * 获取当前声音频谱 
	 * @author mike.zhang
	 * @langversion 3.0 
	 * @playerversion Flash 9 
	 */
	public class SoundProcessor
	{
		public static const LEFT:String="left";
		public static const RIGHT:String="right";
		public static const BOTH:String="both";
		private static var ba:ByteArray=new ByteArray;

		private static var _abs:Boolean=false;
		private static var _m:Boolean=false;

		/**
		 * 是否对频谱取绝对值，取绝对值为true 
		 * @return 取绝对值为true
		 * 
		 */
		public static function get abs ():Boolean
		{
			return _abs;
		}
		public static function set abs (bool:Boolean):void
		{
			_abs=bool;
		}
		
		/**
		 * 是否对频谱值进行加工，使其变化的区间变小。加工为true 
		 * @return 若要对频谱值加工，请设置为true
		 * 
		 */
		public static function get machining():Boolean
		{
			return _m;
		}
		public static function set machining(b:Boolean):void
		{
			_m=b;
		}
		private static function getSection (bArr:ByteArray,sectionLength:uint=512,len:uint=512):Array
		{
			len=len>sectionLength?sectionLength:len;
			var sa:Array=new Array;
			var f:int=Math.floor(sectionLength / len);
			var num:Number;
			for (var i:int=0; i < len; i++)
			{
				var sum:Number=0;
				for (var j:int=0; j < f; j++)
				{
					num=bArr.readFloat();
					if (_abs)
					{
						num=Math.abs(num);
					}
					sum+= num;
				}
				sum/= f;
				if(_m)
				{
					sum=mach(sum);
				}
				sa.push (sum);
			}
			return sa;
		}
		/**
		 * 获取左声道频谱 
		 * @param fourier 一个用于指示是否首先对声音数据执行 Fourier 转换的布尔值。 将此参数设置为 true 会导致方法返回的是频谱而不是原始声音波形。 在频谱中，左侧表示的是低频，右侧表示的是高频。
		 * @param l 获取的长度，最大为256
		 * @return 数组
		 * 
		 */
		public static function getLeftChannel (fourier:Boolean,l:uint=256):Array
		{
			SoundMixer.computeSpectrum (ba,fourier,0);
			return getSection(ba,256,l);
		}
		/**
		 * 获取右声道频谱 
		 * @param fourier 一个用于指示是否首先对声音数据执行 Fourier 转换的布尔值。 将此参数设置为 true 会导致方法返回的是频谱而不是原始声音波形。 在频谱中，左侧表示的是低频，右侧表示的是高频。
		 * @param l 获取的长度，最大为256
		 * @return 数组
		 * 
		 */
		public static function getRightChannel (fourier:Boolean,l:uint=256):Array
		{
			SoundMixer.computeSpectrum (ba,fourier,0);
			ba.position=1024;
			return getSection(ba,256,l);
		}
		/**
		 * 获取频谱
		 * @param fourier 一个用于指示是否首先对声音数据执行 Fourier 转换的布尔值。 将此参数设置为 true 会导致方法返回的是频谱而不是原始声音波形。 在频谱中，左侧表示的是低频，右侧表示的是高频。
		 * @param l 获取的长度，最大为512
		 * @return 数组
		 * 
		 */
		public static function getSoundSpectrum (fourier:Boolean,l:uint=512):Array
		{
			if (l > 512 || Math.sqrt(l) % 2 != 0)
			{
				new ArgumentError("getSoundSpectrum的参数l不能大于512且是2的某次方");
			}
			try
			{
				SoundMixer.computeSpectrum (ba,fourier,0);
			}
			catch (e:Error)
			{
				return new Array  ;
			}
			return getSection(ba,512,l);
		}
		/**
		 * 获取音量 
		 * @param channel 获取音量的范围，可以是左（LEFT），右（RIGHT），全部（BOTH）
		 * @return 音量值(0 － 100)
		 * 
		 */
		public static function getVolume (channel:String=BOTH):Number
		{
			SoundMixer.computeSpectrum (ba,false,0);
			var soundArray:Array=new Array;
			switch (channel)
			{
				case LEFT :
					soundArray=getLeftChannel(true);
					break;
				case RIGHT :
					soundArray=getRightChannel(true);
					break;
				case BOTH :
				default :
					soundArray=getSoundSpectrum(true);
					break;
			}
			var vol:Number=0;
			var l:int=soundArray.length;
			for (var i:int=0; i < l; i++)
			{
				vol+= soundArray[i];
			}
			vol/= l;
			return vol * 100;
		}
		/**
		 * 频谱加工函数 
		 * @param v 频谱值
		 * @return 加工后的值
		 * 
		 */
		private static function mach(v:Number):Number
		{
			var va:Number=Math.abs(v);
			if(va<0.0005)
			{
				v*=1600;
			}else if(va<0.001)
			{
				v*=850
			}else if(va<0.01)
			{
				v*=90;
			}else if(va<0.1)
			{
				v*=9.2;
			}else if(va<0.2)
			{
				v*=0.475;
			}else if(va<0.5)
			{
				v*=2;
			}
			return v;
		}
	}
}